﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Media3D;
using System.Globalization;

namespace Oxage.Simulator
{
	/// <summary>
	/// N-dimensional point.
	/// </summary>
	public class NPoint<T>
	{
		#region Members
		protected Dictionary<int, T> dict = new Dictionary<int, T>();
		#endregion

		#region Constructors
		public NPoint()
		{
		}

		public NPoint(params T[] args)
		{
			for (int i = 0; i < args.Length; i++)
			{
				this[i] = args[i];
			}
		}
		#endregion

		#region Public properties
		public T this[int index]
		{
			get
			{
				if (dict.ContainsKey(index))
				{
					return dict[index];
				}
				return default(T);
			}
			set
			{
				//TODO: Handle nullable values
				if (dict.ContainsKey(index))
				{
					if (!dict[index].Equals(value))
					{
						T previous = dict[index];
						dict[index] = value;
						OnValueChanged(index, previous, value);
					}
				}
				else
				{
					dict.Add(index, value);
					OnValueChanged(index, default(T), value);
				}
			}
		}

		public T X
		{
			get
			{
				return this[0];
			}
			set
			{
				this[0] = value;
			}
		}

		public T Y
		{
			get
			{
				return this[1];
			}
			set
			{
				this[1] = value;
			}
		}

		public T Z
		{
			get
			{
				return this[2];
			}
			set
			{
				this[2] = value;
			}
		}
		#endregion

		#region Public methods
		public IEnumerable<int> GetKeys()
		{
			return dict.Keys;
		}
		#endregion

		#region Event handlers
		public event EventHandler<ValueChangedEventArgs<T>> ValueChanged;
		protected virtual void OnValueChanged(int index, T oldValue, T newValue)
		{
			if (ValueChanged != null)
			{
				ValueChanged(this, new ValueChangedEventArgs<T>(index, oldValue, newValue));
			}
		}
		#endregion
	}

	public class NPoint : NPoint<double>
	{
		#region Constructors
		public NPoint() : base()
		{
		}

		public NPoint(Point point) : base()
		{
			X = point.X;
			Y = point.Y;
		}

		public NPoint(Point3D point) : base()
		{
			X = point.X;
			Y = point.Y;
			Z = point.Z;
		}

		public NPoint(params double[] args) : base(args)
		{
		}
		#endregion

		#region Public methods
		public NPoint GetSignum()
		{
			NPoint point = new NPoint();
			foreach (var dim in this.GetKeys())
			{
				//point[dim] = Math.Sign(this[dim]);

				if (this[dim] == 0)
				{
					point[dim] = 0;
				}
				else if (this[dim] > 0)
				{
					point[dim] = 1;
				}
				else if (this[dim] < 0)
				{
					point[dim] = -1;
				}
			}
			return point;
		}

		public Point GetPoint2D()
		{
			return new Point(X, Y);
		}

		public Point3D GetPoint3D()
		{
			return new Point3D(X, Y, Z);
		}

		public override string ToString()
		{
			return string.Format("({0}, {1}, {2})", X.ToString(CultureInfo.InvariantCulture), Y.ToString(CultureInfo.InvariantCulture), Z.ToString(CultureInfo.InvariantCulture));
		}
		#endregion
	}

	public class HomeNPoint : NPoint
	{
	}

	public class LimitNPoint : NPoint
	{
	}
}
